<?php

namespace app\controller;

use app\BaseController;
use EasyWeChat\MiniApp\Application;
use EasyWeChat\Pay\Application as payApp;
use think\facade\Config;

class Wechat extends BaseController
{

    public function getConfig(): array
    {
        $wechat = Config::get('wechat');
        return [
            'app_id' => $wechat['app_id'],
            'secret' => $wechat['secret'],
            'mch_id' => $wechat['pay']['mch_id'],
            // v2 API 秘钥
            //'v2_secret_key' => $wechat['pay']['v2_key'],
            // v3 API 秘钥
            'secret_key' => $wechat['pay']['v3_key'],
            'certificate' => $wechat['pay']['cert_path'],
            'private_key' => $wechat['pay']['key_path'],
            'notify_url' => $wechat['pay']['notify_url'],
            'http' => [
                'throw' => true, // 状态码非 200、300 时是否抛出异常，默认为开启
                'timeout' => 5.0,
            ],
        ];
    }

    /**
     * 生产不重复的随机字符串
     */
    public function createNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return date('Ymd') . $str;
    }

    /**
     * 获取用户openid
     *
     */
    public function getOpenid()
    {
        $code = $this->request->param('code');
        if ($code) {
            // 创建微信小程序对象
            $wechat = new Application($this->getConfig());
            // 获取 session_key 和 openid
            $result = $wechat->getUtils()->codeToSession($code);
            // 处理结果
            if (isset($result['openid'])) {
                // 业务处理
                return json(['code' => 1, 'data' => $result['openid'], 'msg' => '获取openid成功']);
            } else {
                // 报错 $res['errmsg']
                return json(['code' => 0, 'data' => '', 'msg' => $result['errmsg']]);
            }
        } else {
            return json(['code' => 0, 'data' => '', 'msg' => 'code不能为空']);
        }
    }

    /**
     * 获取用户手机号
     *
     */
    public function getPhoneNumber()
    {
        $phone_code = $this->request->param('phone_code');
        if ($phone_code) {
            // 创建微信小程序对象
            $wechat = new Application($this->getConfig());
            // getPhoneNumber
            $data = ['code' => $phone_code,];
            $result = $wechat->getClient()->postJson('wxa/business/getuserphonenumber', $data);
            if (isset($result['phone_info'])) {
                // 业务处理
                return json(['code' => 1, 'data' => $result['phone_info']['phoneNumber'], 'msg' => '获取手机号成功']);
            } else {
                // 报错 $res['errmsg']
                return json(['code' => 0, 'data' => '', 'msg' => $result['errmsg']]);
            }
        } else {
            return json(['code' => 0, 'data' => '', 'msg' => 'phone_code不能为空']);
        }
    }

    /**
     * 微信支付
     */
    public function pay()
    {
        $open_id = $this->request->param('open_id');
        if ($open_id) {
            // 创建微信支付对象
            $wechat = new payApp($this->getConfig());
            // 第一步：JSAPI 下单，组装请求参数
            $params = [
                'appid' => $this->getConfig()['app_id'],
                'mchid' => $this->getConfig()['mch_id'],
                'description' => '1分钱支付测试',                                     // 商品描述
                'out_trade_no' => $this->createNonceStr(10),                 // 商户订单号
                'attach' => json_encode([]),                                        // 附加数据
                'notify_url' => 'https://www.tp8.com/index.php/wechat/notify',      // 支付结果通知网址，如果不设置则会使用配置里的默认地址
                "amount" => [
                    "total" => 1,                                                   // 订单总金额，单位为分，只能为整数
                    "currency" => "CNY"                                             // 货币类型，默认人民币：CNY，目前只支持该类型
                ],
                "payer" => [
                    "openid" => $open_id
                ]
            ];
            $response = $wechat->getClient()->postJson("v3/pay/transactions/jsapi", $params);
            //print_r($response->toArray());exit;
            $result = $response->toArray(false);
            // 请求失败
            if (!isset($result['prepay_id'])) {
                return json(['code' => 0, 'data' => '', 'msg' => $result['message']]);
            }
            // 第二步：组装小程序调起支付 API
            $prepayId = $result['prepay_id'];
            $appId = $this->getConfig()['app_id'];
            $signType = 'RSA'; // 默认RSA，v2要传MD5
            $config = $wechat->getUtils()->buildMiniAppConfig($prepayId, $appId, $signType);
            return json(['code' => 1, 'data' => $config, 'msg' => '微信支付成功']);
        } else {
            return json(['code' => 0, 'data' => '', 'msg' => 'open_id不能为空']);
        }
    }

    /**
     * 查询订单
     */
    public function checkOrder()
    {
        $out_trade_no = $this->request->param('out_trade_no');
        $transaction_id = $this->request->param('transaction_id');
        // 创建微信支付对象
        $wechat = new payApp($this->getConfig());
        // 查询参数
        $params = [
            'query' => [
                // 微信订单号或者商户订单号
                'mchid' => $this->getConfig()['mch_id']
            ]
        ];
        if ($out_trade_no) {
            // 查询订单
            $response = $wechat->getClient()->get("v3/pay/transactions/out-trade-no/{$out_trade_no}", $params);
            //print_r($response->toArray());exit;
            $result = $response->toArray();
            if (isset($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
                return json(['code' => 1, 'data' => $result, 'msg' => '查询订单成功']);
            } else {
                return json(['code' => 0, 'data' => $result, 'msg' => '查询订单失败']);
            }
        } elseif ($transaction_id) {
            // 查询订单
            $response = $wechat->getClient()->get("v3/pay/transactions/id/{$transaction_id}", $params);
            //print_r($response->toArray());exit;
            $result = $response->toArray();
            if (isset($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
                return json(['code' => 1, 'data' => $result, 'msg' => '查询订单成功']);
            } else {
                return json(['code' => 0, 'data' => $result, 'msg' => '查询订单失败']);
            }
        } else {
            return json(['code' => 0, 'data' => '', 'msg' => 'out_trade_no不能为空']);
        }
    }

    /**
     * 微信退款
     */
    public function refund()
    {
        $out_trade_no = $this->request->param('out_trade_no');
        $transaction_id = $this->request->param('transaction_id');
        $total_fee = $this->request->param('total_fee');
        $refund_fee = $this->request->param('refund_fee');
        if (($out_trade_no || $transaction_id) && $total_fee && $refund_fee) {
            // 创建微信支付对象
            $wechat = new payApp($this->getConfig());
            // 生成商户退款单号
            $out_refund_no = $this->createNonceStr(10);
            // 微信支付金额单位为分，需要*100
            $total_fee = intval($total_fee * 100);
            // 微信退款金额单位为分，需要*100
            $refund_fee = intval($refund_fee * 100);
            // 第一步：组装请求参数
            $params = [
                'out_refund_no' => $out_refund_no,
                'notify_url' => 'https://www.tp8.com/index.php/wechat/refund_notify',
                'amount' => [
                    "refund" => $refund_fee,
                    "total" => $total_fee,
                    "currency" => "CNY"
                ]
            ];
            if ($out_trade_no) {
                $params['out_trade_no'] = $out_trade_no;
            } elseif ($transaction_id) {
                $params['transaction_id'] = $transaction_id;
            }
            // 第二步：发起退款请求
            $response = $wechat->getClient()->postJson("v3/refund/domestic/refunds", $params);
            // 处理退款结果
            $result = $response->toArray(false);
            // 处理退款结果
            if (isset($result['status']) && $result['status'] === 'SUCCESS') {
                // 退款成功，更新订单状态
                return json(['code' => 1, 'data' => $result, 'msg' => '微信退款成功']);
            } else {
                return json(['code' => 0, 'data' => $result, 'msg' => '微信退款失败']);
            }
        } else {
            return json(['code' => 0, 'data' => '', 'msg' => '缺少必要参数']);
        }
    }

    /**
     * 微信支付通知
     *
     */
    public function notify()
    {
        trace('这是一条支付回调日志信息', 'notice');
        // 创建微信小程序对象
        $wechat = new Application($this->getConfig());
        $server = $wechat->getServer();
        $server->handlePaid(function (Message $message, \Closure $next) {
            trace($message, 'notice');
            // $message->out_trade_no 获取商户订单号
            // $message->payer['openid'] 获取支付者 openid
            // 🚨🚨🚨 注意：推送信息不一定靠谱哈，请务必验证
            // 建议是拿订单号调用微信支付查询接口，以查询到的订单状态为准
            // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
            $order = '查询数据库';
            if (!$order) { // 如果订单不存在 或者 订单已经支付过了
                return true; // 告诉微信，我已经处理完了，订单没找到，别再通知我了
            }
            if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态，不代表支付状态
                // 用户是否支付成功
                if ($message['result_code'] === 'SUCCESS') {
                    // 支付成功，更新订单状态

                } elseif ($message['result_code'] === 'FAIL') {
                    // 用户支付失败

                }
            }
            return $next($message);
        });

        // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
        return $server->serve();
    }

    /**
     * 微信退款通知
     *
     */
    public function refund_notify()
    {
        trace('这是一条退款回调日志信息', 'notice');
        // 创建微信小程序对象
        $wechat = new Application($this->getConfig());
        $server = $wechat->getServer();
        $server->handleRefunded(function (Message $message, \Closure $next) {
            trace($message, 'notice');
            // $message->out_trade_no 获取商户订单号
            // $message->payer['openid'] 获取支付者 openid
            return $next($message);
        });

        // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
        return $server->serve();
    }

}
